/** \file osocklib/osocklib.h
 * \brief Otter Socket Library
 * \author Jean Loup Le Roux <nc@nightmare-creature.org>
 *  
 *  This is another POSIX sockets library mainly designed to ease data
 *  sending/retrieval by TCP or UDP broadcast. This library is part of the YOP
 *  project (http://code.google.com/p/yopp2p/) but can be easily integrated in
 *  other projects. 
 *
 * \todo Implement TCP Multicast
 */

#ifndef OSOCKLIB_H
#define OSOCKLIB_H

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>

/// Maximum network packet size (in bytes)
#define MAX_NPACK_SIZE 4096

/// Minimum port to start research of free UDP ports from (not supposed to go
//below 1024)
#define MIN_PORT 1024

/// Port limit  
#define PORT_LIMIT 65535

/// Number of seconds to listen on each UDP port while searching free ports
#define UDP_SEARCH_TIMEOUT 2

/// During free UDP port search, every time we fail, randomly increment port by this value - 1 (at best) 
#define MAX_PORT_INCR 11 

/// Select a particular protocol whithin protocol family (0 is auto)
#define SELECT_PROTOCOL 0

/// TCP and UDP flags for recvfrom() function
#define RECV_TCP_FLAGS 0
#define RECV_UDP_FLAGS 0

/// TCP and UDP flags for sendto() function
#define SENDTO_TCP_FLAGS 0
#define SENDTO_UDP_FLAGS 0

/// Number of pending connections in TCP Server mode
#define LISTEN_PENDING_CONNECT 0

/** \brief Protocol enum 
 * One must choose between TCP or UDP when creating a meta socket descriptor.
 */
typedef enum _protocol_t {
  TCP = 1,
  UDP
} protocol_t;

/** \brief Meta socket mode enum
 * One must choose between Client or Server when creating a meta socket
 * descriptor.
 */
typedef enum _sockmode_t {
  CLIENT = 1,
  SERVER
} sockmode_t;

/** \brief Network packet structure
 * This is an encapsulation of sent or received data via a meta socket.
 */
typedef struct _npacket_s {
  void* datap;
  size_t datalen;
} npacket_s;

/** \brief Meta socket descriptor structure 
 * This is an encapsulation of everything needed to
 * define a CLIENT or SERVER.
 */
typedef struct _msd_s {
  /// select protocol family 
  int domain; 
  /// select communication semantics (stream, datagram, ...)
  int type; 
  /// specifies a particular protocol within protocol family (0 is auto)
  int protocol; 
  /// used to store IP, Family and Port information
  struct sockaddr_in addr; 
  /// POSIX socket descriptor
  int sd; 
  /// Mode (either SERVER or CLIENT)
  sockmode_t mode;
} msd_s;

/** \brief Free UDP broadcast port research 
 * \param begin_at_port apply research algorithm from this port (can be ignored
 * by providing 0)
 * \return free UDP port or -1
 */
int OSOCK_get_free_udp_port(unsigned int begin_at_port);

/** \brief Subnet checker
 * This function tests if provided IPv4 address is on the same subnet.
 * \param ipv4 remote host IPv4 address
 * \warning Always returns success.
 * \todo Implement subnet test using either: ioctls, subnet mask bitwise
 * operations, or any smart function already provided by the system.
 * \return 0 (success) or -1
 */
int OSOCK_same_subnet(const char* ipv4);

/** \brief Meta socket creation function 
 * \param msocket metasocket structure to fill
 * \param proto either TCP or UDP
 * \param ipv4addr IPv4 address, if needed (numbers-and-dots format)
 * \param port communication port, should be > 1024 to avoid problems with other
 * services that might be already running
 * \param mode either CLIENT or SERVER 
 * \return 0 (success) or -1
 * \note domain = PF_INET6 (IPv6) will be available later on
 * \note OSOCK_connect() is a non blocking function.
 * \warning One must "enable" every TCP SERVER created by OSOCK_connect() by
 * using OSOCK_validate_tcp_server(), which is a blocking function.
 * 
 * \note Create a TCP server listening on port 2000:
 * OSOCK_connect((msd_s*) msocket, TCP, NULL, 2000, SERVER);
 * OSOCK_validate_tcp_server((msd_s*) msocket);
 * 
 * \note Create a TCP client connecting on 26.13.4.76:2000:
 * char ipv4addr[] = "26.13.4.76"; OSOCK_connect((msd_s*) msocket, TCP, (const char*) &ipv4addr, 2000, CLIENT);
 * 
 * \note Create an UDP server broadcasting data on port 2000:
 * OSOCK_connect((msd_s*) msocket, UDP, NULL, 2000, SERVER);
 * 
 * \note Create an UDP client on port 2000 (to receive broadcasted data on this port):
 * OSOCK_connect((msd_s*) msocket, UDP, NULL, 2000, CLIENT);
 */
int OSOCK_connect(msd_s* msocket, 
                  const protocol_t proto, 
                  const char* ipv4addr, 
                  in_port_t port, 
                  const sockmode_t mode);

/** \brief Validate TCP SERVER Meta socket
 * \param msocket Meta socket describing a TCP SERVER to enable
 * \return 0 (success) or -1
 * \note This is a blocking function.
 * \warning One must use this function after creating a TCP SERVER Meta socket.
 */
int OSOCK_validate_tcp_server(msd_s* msocket);

/** \brief Metasocket closing function
 * \param msocket metasocket to close
 * \return 0 (success) or -1
 */
int OSOCK_disconnect(msd_s* msocket);

/** \brief Sending network packet via meta socket function
 * \param msocket metasocket to send through
 * \param packet network packet to send
 * \return 0 (success) or -1
*/
int OSOCK_send(const msd_s* msocket, const npacket_s* packet);

/** \brief Receiving network packet via meta socket function
 * \param msocket metasocket to receive from
 * \param packet network packet to fill 
 * \return -1 (error), 0 (nothing received or orderly shutdown from remote
 * host or received more than expected), 1 (success)
 */
int OSOCK_receive(npacket_s* packet, const msd_s* msocket);

/** \brief Fill network packet structure
 * \param packet network packet to fill
 * \param data pointer to data
 * \param len length of data to store in packet
 * \return 0 (success) or -1
 */
int OSOCK_fill_npacket(npacket_s* packet, const void* data, const size_t len);

/** \brief Get network packet data length
 * \param packet network packet to get length from
 * \return length of network packet or -1
 */
size_t OSOCK_get_npacket_length(const npacket_s* packet);

/** \brief Get network packet data pointer
 * \param packet network packet to get data pointer from
 * \return pointer to network data or NULL
 */
void* OSOCK_get_npacket_data(npacket_s* packet);

/** \brief Monitors a TCP SERVER metasocket for incoming data
 * \param msocket Meta socket to monitor
 * \param timeout maximum time to wait for incoming data (0 is infinite)
 * \return 0 if there are data to read, -1 otherwize (see debug file for more info)
 * \warning This is a blocking function.
 */
int OSOCK_watch_connection(const msd_s* msocket, long timeout);

#endif

/* end of osocklib.h */
